
#include <string.h>
#include <map>

#include <btBulletCollisionCommon.h>
#include <btBulletDynamicsCommon.h>
#include <BulletCollision/CollisionShapes/btShapeHull.h>

#include "../defines.hpp"
#include "../sys/Resources.hpp"
#include "ModelManager.hpp"

#include "CollisionShapeManager.hpp"

namespace zen {

	namespace video {

		PCollisionShape CollisionShapeManager::loadFile(const std::string &fileName, PModel model) {
			return loadFile(fileName, fileName, model);
		}

		PCollisionShape CollisionShapeManager::loadFile(const std::string &id, const std::string &fileName, PModel model) {
			PCollisionShape shape = getShape(id);
			if (shape.get() == NULL) {
				jsl::Node* node = zen::sys::Resources::load(fileName);
				shape = PCollisionShape(createFromJSL(node, model.get()));
				delete node;
				addShape(id, shape);
			}
			return shape;
		}
		
		PCollisionShape CollisionShapeManager::loadNode(const std::string &id, const jsl::Node* node, PModel model) {
			PCollisionShape shape = getShape(id);
			if (shape.get() == NULL) {
				shape = PCollisionShape(createFromJSL(node, model.get()));
				addShape(id, shape);
			}
			return shape;
		}

		btCollisionShape* CollisionShapeManager::createFromJSL(const jsl::Node* node, zen::video::Model* model) {
			if (node == NULL)
				return NULL;
			btCollisionShape* shape = NULL;
			std::string type = node->at("type")->asString();
			if (type == "compound") {
				shape = new btCompoundShape();
				jsl::JSLarray &shapes = *node->at("compound")->asArray();
				btMatrix3x3 tmpbasis;
				btVector3 tmporigin;
				try {
					for (unsigned int i = 0; i < shapes.size(); i++) {
						jsl::Node* tmp = shapes[0]->at("local");
						if (tmp->type() == jsl::StringType) {
                    		if (tmp->asString() == "identity") {
                    			tmpbasis.setIdentity();
                    			tmporigin.setValue(btScalar(0.0), btScalar(0.0), btScalar(0.0));
                    		}
						} else {
							try {
                        		jsl::JSLarray &local = *tmp->asArray();
								tmpbasis.setValue(
									local[0]->asFloat(), local[4]->asFloat(), local[8]->asFloat(),
									local[1]->asFloat(), local[5]->asFloat(), local[9]->asFloat(),
									local[2]->asFloat(), local[6]->asFloat(), local[10]->asFloat());
								tmporigin.setValue(
                            		local[12]->asFloat(), local[13]->asFloat(), local[14]->asFloat());
							} catch (const std::exception &) {
								tmpbasis.setIdentity();
								tmporigin.setValue(btScalar(0.0), btScalar(0.0), btScalar(0.0));
							}
						}
						tmp = shapes[i]->at("shape");
						btCollisionShape* childShape = createFromJSL(tmp, model);
						if (childShape != NULL)
							((btCompoundShape*)shape)->addChildShape(btTransform(tmpbasis, tmporigin), childShape);
					}
				} catch (const std::exception &) {
					delete shape;
					shape = NULL;
				}
			} else if (type == "convexHull") {
				if (model == NULL)
					return NULL;
				jsl::Node* tmp = node->at("mesh");
				if (tmp == NULL || tmp->asString() == "")
					return NULL;
				zen::video::model::Mesh* mesh = model->getMeshByBone(tmp->asString());
				if (mesh == NULL)
					return NULL;
				btConvexHullShape* tmpchull = new btConvexHullShape((glm::f32*)mesh->getVertexes(), mesh->getVertexCount(), mesh->getVertexSize());
				btShapeHull* tmphull = new btShapeHull(tmpchull);
				tmphull->buildHull(tmpchull->getMargin());
				shape = new btConvexHullShape((btScalar*)tmphull->getVertexPointer(), tmphull->numVertices());
				delete tmphull;
				delete tmpchull;
			} else if (type == "sphere") {
				btScalar radius;
				radius = node->at("radius")->asFloat();
				shape = new btSphereShape(radius);
			} else if (type == "box") {
				btScalar xext, yext, zext;
				xext = node->at("xHalfExtent")->asFloat();
				yext = node->at("yHalfExtent")->asFloat();
				zext = node->at("zHalfExtent")->asFloat();
				shape = new btBoxShape(btVector3(xext, yext, zext));
			}
			return shape;
		}


	} // namespace video

} // namespace zen
